<template>
  <div class="base-info">
    <a-form
      :key="formKey"
      ref="formRef"
      :model="form"
      :rules="rules"
      label-align="left"
      :labelCol="{ style: 'width: 92px' }"
      :wrapperCol="{ style: 'width: 480px' }"
    >
      <!-- 应用名称 -->
      <a-form-item name="appName" label="应用名称" required>
        <a-input v-model:value="form.appName" placeholder="请输入节点名称" allow-clear style="width: 480px" />
      </a-form-item>

      <!-- 所属节点 -->
      <a-form-item name="node" label="所属节点" required>
        <a-select v-model:value="form.node" placeholder="请选择" :options="nodeOptions" style="width: 480px" allow-clear />
      </a-form-item>

      <!-- 应用图标 -->
      <a-form-item name="logo" label="应用图标" :validateTrigger="['change','blur']">
        <a-upload
          class="logo-upload"
          :class="{ 'hide-select': fileList.length >= 1 }"
          list-type="picture-card"
          :file-list="fileList"
          :beforeUpload="onBeforeUpload"
          :max-count="1"
          accept="image/*"
          :showUploadList="{ showPreviewIcon: false, showRemoveIcon: true }"
          @remove="onRemoveFile"
        >
          <transition name="fade-zoom">
            <div v-if="fileList.length < 1 && !uploading" class="upload-tile">
              <span class="plus">+</span>
              <div class="upload-text">上传</div>
            </div>
          </transition>
        </a-upload>
      </a-form-item>
    </a-form>
  </div>
</template>

<script setup lang="ts">
import { ref, watch, nextTick } from 'vue'
import { message } from 'ant-design-vue'
import type { Rule } from 'ant-design-vue/es/form'
import api from '@/api/api'

const props = defineProps<{ initial: any | null }>()

type FormModel = { appName: string; node: string | undefined; logo?: File | null }

const formRef = ref()
const form = ref<FormModel>({ appName: '', node: undefined, logo: null })
const formKey = ref(0)

/** 上传预览 远端地址 */
const fileList = ref<any[]>([])
const currentBlobUrl = ref<string | null>(null)
const remoteLogoUrl = ref<string | null>(null)
const uploading = ref(false)
const animDuration = 180

/** 图标 */
function hasLogo() {
  return !!remoteLogoUrl.value || (fileList.value && fileList.value.length > 0)
}

function toPreviewItem(url: string) {
  return { uid: String(Date.now()), name: '', status: 'uploading', url, thumbUrl: url, originFileObj: undefined }
}
function revokeBlob(url?: string | null) {
  if (url && url.startsWith('blob:')) { try { URL.revokeObjectURL(url) } catch { } }
}
function revokeOldBlob() {
  const old = fileList.value?.[0]
  if (old?.url?.startsWith('blob:')) revokeBlob(old.url)
}

// 上传图片
async function onBeforeUpload(file: File) {
  if (!file.type.startsWith('image/')) { message.error('只能上传图片文件'); return false }

  uploading.value = true
  form.value = { ...form.value, logo: file }

  const blobUrl = URL.createObjectURL(file)
  currentBlobUrl.value = blobUrl
  remoteLogoUrl.value = null 
  if (fileList.value.length) {
    const it = fileList.value[0]
    it.status = 'uploading'
    it.url = blobUrl
    it.thumbUrl = blobUrl
    it.name = ''
    fileList.value = [...fileList.value]
  } else {
    fileList.value = [toPreviewItem(blobUrl)]
  }

  try {
    const fd = new FormData()
    fd.append('file', file)

    const uploadFn =
      (api as any)?.post?.searchAppUploadImage
      ?? (api as any)?.file?.searchAppUploadImage
      ?? (api as any)?.searchAppUploadImage

    if (typeof uploadFn !== 'function') {
      throw new Error('')
    }

    const res: any = await uploadFn(fd)
    const payload = res?.data ?? res
    const dataObj = payload?.data ?? payload
    const serverUrl: string | undefined = dataObj?.url || dataObj?.filename

    const it = fileList.value[0]
    if (it) {
      it.status = serverUrl ? 'done' : 'error'
      it.responseUrl = serverUrl || ''
      fileList.value = [...fileList.value]
    }

    if (serverUrl) {
      remoteLogoUrl.value = serverUrl
      message.success('图片上传成功')
    } else {
      message.warning('图片已选择，已使用本地预览')
    }
  } catch (err: any) {
    // 回滚
    const toRevoke = currentBlobUrl.value
    currentBlobUrl.value = null
    revokeBlob(toRevoke)

    fileList.value = []
    form.value = { ...form.value, logo: null }
    remoteLogoUrl.value = null
    message.error(err?.message || '图片上传失败，请重试')
  } finally {
    uploading.value = false
    // 上传结束
    nextTick(() => formRef.value?.validateFields?.(['logo']).catch(() => {}))
  }
  return false
}

function onRemoveFile(file: any) {
  setTimeout(() => {
    revokeBlob(file?.url)
    fileList.value = []
    form.value = { ...form.value, logo: null }
    remoteLogoUrl.value = null
    currentBlobUrl.value = null
    formRef.value?.validateFields?.(['logo']).catch(() => {})
  }, animDuration)
  return true
}

/** 下拉选项 & 校验 */
const nodeOptions = ref<{ label: string; value: string }[]>([
  { label: 'haerbinun', value: 'haerbinun' },
  { label: 'infocf', value: 'infocf' },
  { label: 'xibu', value: 'xibu' }
])

const rules: Record<string, Rule[]> = {
  appName: [{ required: true, message: '请输入应用名称' }],
  node: [{ required: true, message: '请选择所属节点' }],
  logo: [{
    validator: async () => {
      if (hasLogo()) return Promise.resolve()
      return Promise.reject(new Error('请选择应用图标'))
    },
    trigger: ['change', 'blur']
  }]
}

async function validate() {
  await formRef.value?.validate()
}

function normalizeInitial(src: any): { appName: string; node: string; logoUrl?: string } | null {
  if (!src) return null

  const maybeNested = src.search_app || src.searchApp
  if (maybeNested) {
    return {
      appName: String(maybeNested.app_name ?? maybeNested.appName ?? ''),
      node: String(maybeNested.node_name ?? maybeNested.node ?? ''),
      logoUrl: maybeNested.app_icon ?? maybeNested.logo ?? maybeNested.logoUrl
    }
  }

  if ('app_name' in src || 'node_name' in src || 'app_icon' in src) {
    return {
      appName: String(src.app_name ?? ''),
      node: String(src.node_name ?? ''),
      logoUrl: src.app_icon
    }
  }

  if ('appName' in src || 'node' in src || 'logoUrl' in src) {
    return {
      appName: String(src.appName ?? ''),
      node: String(src.node ?? ''),
      logoUrl: src.logoUrl
    }
  }

  return null
}

/** 应用回显 */
async function applyInitial(raw: any) {
  const init = normalizeInitial(raw)
  if (!init) return

  try { formRef.value?.resetFields?.() } catch { }
  try { formRef.value?.clearValidate?.() } catch { }

  form.value = {
    appName: init.appName || '',
    node: init.node ? String(init.node) : undefined,
    logo: null
  }

  if (form.value.node && !nodeOptions.value.some(o => o.value === form.value.node)) {
    nodeOptions.value.push({ label: String(form.value.node), value: String(form.value.node) })
  }

  await nextTick()
  if (init.logoUrl) {
    revokeOldBlob()
    currentBlobUrl.value = null
    remoteLogoUrl.value = init.logoUrl
    fileList.value = [{
      uid: String(Date.now()),
      name: '',
      status: 'done',
      url: init.logoUrl,
      thumbUrl: init.logoUrl
    }]
  } else {
    fileList.value = []
    remoteLogoUrl.value = null
    revokeOldBlob()
  }

  await nextTick()
  try { formRef.value?.clearValidate?.(['logo']) } catch { }

  formKey.value += 1
}

/** 图标状态变化时自动同步校验样式 */
watch([fileList, remoteLogoUrl], () => {
  if (hasLogo()) {
    formRef.value?.clearValidate?.(['logo'])
  } else {
    // 没有图标时，保持错误提示
    formRef.value?.validateFields?.(['logo']).catch(() => {})
  }
})

watch(() => props.initial, (v) => applyInitial(v), { deep: true, immediate: true })

defineExpose({
  validate,
  getBaseInfo() {
    const logoUrl: string | undefined = remoteLogoUrl.value || fileList.value?.[0]?.url
    const logoFile: File | null | undefined = form.value.logo || undefined
    return {
      appName: form.value.appName.trim(),
      node: String(form.value.node || ''),
      logoUrl,
      logoFile
    }
  }
})
</script>

<style scoped lang="less">
:deep(.ant-form-item) {
  margin-bottom: 18px;
}

:deep(.ant-input),
:deep(.ant-select),
:deep(.ant-select-selector) {
  width: 480px;
}

.logo-upload :deep(.ant-upload-list-picture-card-container),
.logo-upload :deep(.ant-upload.ant-upload-select-picture-card) {
  width: 110px;
  height: 110px;
  border-radius: 8px;
}

/* 让预览图沾满且不显示文件名，避免文字/闪烁 */
.logo-upload :deep(.ant-upload-list-picture-card .ant-upload-list-item) {
  padding: 0;
  border-radius: 8px;
  box-shadow: 0 0 0 1px #e9e9e9 inset;
}

.logo-upload :deep(.ant-upload-list-item-thumbnail) {
  width: 100%;
  height: 100%;
}

.logo-upload :deep(.ant-upload-list-item-thumbnail img) {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.logo-upload :deep(.ant-upload-list-item-name) {
  display: none;
}

.logo-upload.hide-select :deep(.ant-upload-select-picture-card) {
  display: none !important;
}

.logo-upload :deep(.ant-upload.ant-upload-select-picture-card) {
  border: 1px dashed #b7c7ff;
  background: #fafcff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.upload-tile {
  text-align: center;
  user-select: none;
}

.upload-tile .plus {
  font-size: 24px;
  line-height: 1;
  display: block;
}

.upload-tile .upload-text {
  margin-top: 6px;
}

.logo-upload :deep(.ant-upload-list-item-actions) {
  opacity: 1;
}

/* 过渡 */
.fade-zoom-enter-active,
.fade-zoom-leave-active {
  transition: opacity .18s ease, transform .18s ease;
}

.fade-zoom-enter-from,
.fade-zoom-leave-to {
  opacity: 0;
  transform: scale(.96);
}
</style>
